/*
 * Copyright 2008 Sun Microsystems, Inc.  All Rights Reserved.
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * This code is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License version 2 only, as
 * published by the Free Software Foundation.  Sun designates this
 * particular file as subject to the "Classpath" exception as provided
 * by Sun in the LICENSE file that accompanied this code.
 *
 * This code is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 * version 2 for more details (a copy is included in the LICENSE file that
 * accompanied this code).
 *
 * You should have received a copy of the GNU General Public License version
 * 2 along with this work; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 *
 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
 * CA 95054 USA or visit www.sun.com if you need additional information or
 * have any questions.
 */
package com.sun.lwuit.plaf;

import com.sun.lwuit.TextField;
import com.sun.lwuit.geom.Dimension;
import com.sun.lwuit.Graphics;
import com.sun.lwuit.Image;
import com.sun.lwuit.Button;
import com.sun.lwuit.CheckBox;
import com.sun.lwuit.ComboBox;
import com.sun.lwuit.Component;
import com.sun.lwuit.Label;
import com.sun.lwuit.List;
import com.sun.lwuit.RadioButton;
import com.sun.lwuit.RichTextArea;
import com.sun.lwuit.TextArea;
import com.sun.lwuit.WrappingTextArea;
import com.sun.lwuit.list.ListCellRenderer;
import com.sun.lwuit.list.ListModel;
import com.sun.lwuit.Font;
import com.sun.lwuit.TabbedPane;
import com.sun.lwuit.events.FocusListener;
import com.sun.lwuit.geom.Rectangle;
import com.sun.lwuit.RichTextArea;
import com.bluewhalesystems.client.lwuit.richtext.*;
import java.util.Calendar;
import java.util.Date;

/**
 * Used to render the default look of LWUIT
 *
 * @author Chen Fishbein
 */
public class DefaultLookAndFeel extends LookAndFeel implements FocusListener {
    private static final long DAY = 1000 * 60 * 60 * 24;
    private Image[] chkBoxImages = null;
    private Image comboImage = null;
    private Image[] rButtonImages = null;
    private static int DAY_SPACE_W = 12;
    private static int DAY_SPACE_H = 15;
    private boolean tickWhenFocused = true;

    /** Creates a new instance of DefaultLookAndFeel */
    public DefaultLookAndFeel() {
    }

    /**
     * @inheritDoc
     */
    public void bind(Component cmp) {
        if (tickWhenFocused && cmp instanceof Label) {
            ((Label) cmp).addFocusListener(this);
        }
    }
    /**
     * This method allows to set all Labels, Buttons, CheckBoxes, RadioButtons
     * to start ticking when the text is too long. 
     * @param tickWhenFocused
     */
    public void setTickWhenFocused(boolean tickWhenFocused) {
        this.tickWhenFocused = tickWhenFocused;
    }

    /**
     * Sets images for checkbox checked/unchecked modes
     * 
     * @param checked the image to draw in order to represent a checked checkbox
     * @param unchecked the image to draw in order to represent an uncheck checkbox
     */
    public void setCheckBoxImages(Image checked, Image unchecked) {
        if (checked == null || unchecked == null) {
            chkBoxImages = null;
        } else {
            chkBoxImages = new Image[]{unchecked, checked};
        }
    }

    /**
     * Sets image for the combo box dropdown drawing
     * 
     * @param picker picker image
     */
    public void setComboBoxImage(Image picker) {
        comboImage = picker;
    }

    /**
     * Sets images for radio button selected/unselected modes
     * 
     * @param selected the image to draw in order to represent a selected radio button
     * @param unselected the image to draw in order to represent an unselected radio button
     */
    public void setRadioButtonImages(Image selected, Image unselected) {
        if (selected == null || unselected == null) {
            rButtonImages = null;
        } else {
            rButtonImages = new Image[]{unselected, selected};
        }
    }

    /**
     * Returns the images used to represent the radio button (selected followed by unselected).
     * 
     * @return images representing the radio button or null for using the default drawing
     */
    public Image[] getRadioButtonImages() {
        return rButtonImages;
    }

    /**
     * Returns the images used to represent the checkbox (selected followed by unselected).
     *
     * @return images representing the check box or null for using the default drawing
     */
    public Image[] getCheckBoxImages() {
        return chkBoxImages;
    }

    /**
     * @inheritDoc
     */
    public void drawButton(Graphics g, Button b) {
        drawComponent(g, b, b.getIconFromState(), null, 0);
    }

    /**
     * @inheritDoc
     */
    public void drawCheckBox(Graphics g, CheckBox cb) {
        if (chkBoxImages != null) {
            drawComponent(g, cb, cb.getIconFromState(), chkBoxImages[cb.isSelected() ? 1 : 0], 0);
        } else {
            Style style = cb.getStyle();
            int height = cb.getHeight();
            drawComponent(g, cb, cb.getIconFromState(), null, height);

            int gradientColor;
            g.setColor(style.getFgColor());
            gradientColor = style.getBgColor();

            int width = height;

            int tX = cb.getX() + style.getPadding(Component.LEFT);
            int tY = cb.getY() + style.getPadding(Component.TOP) + (cb.getHeight() - style.getPadding(Component.TOP) - style.getPadding(Component.BOTTOM)) / 2 - height / 2;
            g.translate(tX, tY);
            int x = scaleCoordinate(1.04f, 16, width);
            int y = scaleCoordinate(4.0f, 16, height);
            int rectWidth = scaleCoordinate(10.9515f, 16, width);
            int rectHeight = scaleCoordinate(10f, 16, height);

            // brighten or darken the color slightly
            int destColor = findDestColor(gradientColor);

            g.fillLinearGradient(gradientColor, destColor, x + 1, y + 1, rectWidth - 2, rectHeight - 1, false);
            g.drawRoundRect(x, y, rectWidth, rectHeight, 5, 5);

            if (cb.isSelected()) {
                int color = g.getColor();
                g.setColor(0x111111);
                g.translate(0, 1);
                fillCheckbox(g, width, height);
                g.setColor(color);
                g.translate(0, -1);
                fillCheckbox(g, width, height);
            }
            g.translate(-tX, -tY);
        }
    }

    private static void fillCheckbox(Graphics g, int width, int height) {
        int x1 = scaleCoordinate(2.0450495f, 16, width);
        int y1 = scaleCoordinate(9.4227722f, 16, height);
        int x2 = scaleCoordinate(5.8675725f, 16, width);
        int y2 = scaleCoordinate(13.921746f, 16, height);
        int x3 = scaleCoordinate(5.8675725f, 16, width);
        int y3 = scaleCoordinate(11f, 16, height);
        g.fillTriangle(x1, y1, x2, y2, x3, y3);

        x1 = scaleCoordinate(14.38995f, 16, width);
        y1 = scaleCoordinate(0.88766801f, 16, height);
        g.fillTriangle(x1, y1, x2, y2, x3, y3);
    }

    private static int round(float x) {
        int rounded = (int) x;
        if (x - rounded > 0.5f) {
            return rounded + 1;
        }
        return rounded;
    }

    /**
     * Takes a floating point coordinate on a virtual axis and rusterizes it to 
     * a coordinate in the pixel surface. This is a very simple algorithm since
     * anti-aliasing isn't supported.
     * 
     * @param coordinate a position in a theoretical plain
     * @param plain the amount of space in the theoretical plain
     * @param pixelSize the amount of pixels available on the screen
     * @return the pixel which we should color
     */
    private static int scaleCoordinate(float coordinate, float plain, int pixelSize) {
        return round(coordinate / plain * pixelSize);
    }

    /**
     * @inheritDoc
     */
    public void drawLabel(Graphics g, Label l) {
        drawComponent(g, l, l.getIcon(), null, 0);
    }

    /**
     * @inheritDoc
     */
    public void drawRadioButton(Graphics g, RadioButton rb) {
        if (rButtonImages != null) {
            drawComponent(g, rb, rb.getIconFromState(), rButtonImages[rb.isSelected() ? 1 : 0], 0);
        } else {
            Style style = rb.getStyle();
            int height = rb.getHeight();

            drawComponent(g, rb, rb.getIconFromState(), null, height + rb.getGap());
            g.setColor(style.getFgColor());
            height -= (style.getPadding(Component.TOP) + style.getPadding(Component.BOTTOM));
            int x = rb.getX() + style.getPadding(Component.LEFT);
            int y = rb.getY() + style.getPadding(Component.TOP);

            g.drawArc(x, y, height, height, 0, 360);
            if (rb.isSelected()) {
                int color = g.getColor();
                int destColor = findDestColor(color);
                g.fillRadialGradient(color, destColor, x + 3, y + 3, height - 5, height - 5);
            }
        }
    }

    /**
     * @inheritDoc
     */
    public void drawComboBox(Graphics g, ComboBox cb) {
        int border = 2;
        Style style = cb.getStyle();
        int leftPadding = style.getPadding(Component.LEFT);
        int rightPadding = style.getPadding(Component.RIGHT);

        setFG(g, cb);

        ListModel model = cb.getModel();
        ListCellRenderer renderer = cb.getRenderer();
        Object value = model.getItemAt(model.getSelectedIndex());
        int comboImageWidth;
        if (comboImage != null) {
            comboImageWidth = comboImage.getWidth();
        } else {
            comboImageWidth = style.getFont().getHeight();
        }
        if (model.getSize() > 0) {
            Component cmp = renderer.getListCellRendererComponent(cb, value, model.getSelectedIndex(), cb.hasFocus());
            cmp.setX(cb.getX() + leftPadding);
            cmp.setY(cb.getY() + style.getPadding(Component.TOP));
            cmp.setWidth(cb.getWidth() - comboImageWidth - 2 * rightPadding - leftPadding);
            cmp.setHeight(cb.getHeight() - style.getPadding(Component.TOP) - style.getPadding(Component.BOTTOM));
            cmp.paint(g);
        }

        g.setColor(style.getBgColor());
        int y = cb.getY();
        int height = cb.getHeight();
        int width = comboImageWidth + border;
        int x = cb.getX() + cb.getWidth() - comboImageWidth - rightPadding - border;
        if (comboImage != null) {
            g.drawImage(comboImage, x, y + height / 2 - comboImage.getHeight() / 2);
        } else {
            int color = g.getColor();

            // brighten or darken the color slightly
            int destColor = findDestColor(color);

            g.fillLinearGradient(g.getColor(), destColor, x, y, width, height, false);
            g.setColor(color);
            g.drawRect(x, y, width, height - 1);

            width--;
            height--;

            //g.drawRect(x, y, width, height);
            g.translate(x + 1, y + 1);
            g.setColor(0x111111);
            int x1 = scaleCoordinate(2.5652081f, 16, width);
            int y1 = scaleCoordinate(4.4753664f, 16, height);
            int x2 = scaleCoordinate(8.2872691f, 16, width);
            int y2 = scaleCoordinate(10f, 16, height);
            int x3 = scaleCoordinate(13.516078f, 16, width);
            int y3 = y1;
            g.fillTriangle(x1, y1, x2, y2, x3, y3);
            g.translate(-1, -1);
            g.setColor(style.getFgColor());
            g.fillTriangle(x1, y1, x2, y2, x3, y3);
            //g.setColor(style.getFgColor());
            //g.fillTriangle(x1 + 2, y1 + 2, x2, y2 - 2, x3 - 2, y3 + 2);

            g.translate(-x, -y);
        }

    }

    /**
     * Finds a suitable destination color for gradient values
     */
    private int findDestColor(int color) {
        // brighten or darken the color slightly
        int sourceR = color >> 16 & 0xff;
        int sourceG = color >> 8 & 0xff;
        int sourceB = color & 0xff;
        if (sourceR > 128 && sourceG > 128 && sourceB > 128) {
            // darken
            sourceR = Math.max(sourceR >> 1, 0);
            sourceG = Math.max(sourceG >> 1, 0);
            sourceB = Math.max(sourceB >> 1, 0);
        } else {
            // special case for black, since all channels are 0 it can't be brightened properly...
            if(color == 0) {
                return 0x222222;
            }
            
            // brighten
            sourceR = Math.min(sourceR << 1, 0xff);
            sourceG = Math.min(sourceG << 1, 0xff);
            sourceB = Math.min(sourceB << 1, 0xff);
        }
        return ((sourceR << 16) & 0xff0000) | ((sourceG << 8) & 0xff00) | (sourceB & 0xff);
    }

    /**
     * @inheritDoc
     */
    public void drawList(Graphics g, List l) {
    }

    /**
     * @inheritDoc
     */
    public void drawMonthView(Graphics g, com.sun.lwuit.Calendar cal, Component mv) {
        drawBorder(g, mv, 0x020202, 0x080808, 2);
        setFG(g, mv);

        Font f = mv.getStyle().getFont();
        int fh = f.getHeight();

        int w = mv.getWidth();
        int h = mv.getHeight();
        int labelH = (fh + DAY_SPACE_H);
        int ch = h - labelH;

        int dayWidth = (w / 7);
        int dayHeight = (ch / 6);

        long sd = cal.getSelectedDay();
        Date date = new Date();

        int fix = f.stringWidth("22") / 2;


        g.setColor(mv.getStyle().getBgColor());
        g.fillRect(mv.getX() + 2, mv.getY() + 2, mv.getWidth() - 4, labelH-4);

        g.setColor(mv.getStyle().getFgColor());
        g.setFont(mv.getStyle().getFont());

        String[] days = {"Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", 
        "Friday", "Saturday"};
        String[] labels = {"Su", "M", "Tu", "W", "Th", "F", "Sa"};
        
        Style dayStyle = new Style(mv.getStyle());
        dayStyle.setFgColor(mv.getSelectedStyle().getFgColor());
        dayStyle.setPadding(0, 0, fix+2, 0);
        dayStyle.setMargin(0, 0, 0, 0);
        
        Label tmp = new Label();
        tmp.setUnSelectedStyle(dayStyle);
        tmp.setPreferredSize(new Dimension(dayWidth, labelH));
        for (int i = 0; i < labels.length; i++) {
            String day = UIManager.getInstance().localize(days[i], labels[i]);
            tmp.setText(day);
            int dx = (dayWidth * i);
            tmp.setX(mv.getX() + dx);
            tmp.setY(mv.getY());
            tmp.setSize(tmp.getPreferredSize());
            drawLabel(g, tmp);
        }
        paintDates(g, date, mv, sd, f, dayWidth, dayHeight,
                labelH, fix, fh, sd);
    }

    /**
     * @inheritDoc
     */
    public void drawTextArea(Graphics g, TextArea ta) {
        setFG(g, ta);
        int line = ta.getLines();
        int oX = g.getClipX();
        int oY = g.getClipY();
        int oWidth = g.getClipWidth();
        int oHeight = g.getClipHeight();
        Font f = ta.getStyle().getFont();
        int fontHeight = f.getHeight();
        
        if(ta instanceof WrappingTextArea && ((WrappingTextArea)ta).haveErrorsInWrappingLogicOccurred())
        {
            g.setColor( 0xDD9999 );
        }
        
        for (int i = 0; i < line; i++) {
            int x = ta.getX() + ta.getStyle().getPadding(Component.LEFT);
            int y = ta.getY() + ta.getStyle().getPadding(Component.TOP) +
                    (ta.getRowsGap() + fontHeight) * i;
            if(Rectangle.intersects(x, y, ta.getWidth(), fontHeight, oX, oY, oWidth, oHeight)) {
                String rowText = (String) ta.getTextAt(i);
                //display ******** if it is a password field
                String displayText = "";
                if ((ta.getConstraint() & TextArea.PASSWORD) != 0) {
                    for (int j = 0; j < rowText.length(); j++) {
                        displayText += "*";
                    }
                } else {
                    displayText = rowText;
                }

            
                g.drawString(displayText, x, y);
            }
        }
    }

    /**
     * @inheritDoc
     */
    public Dimension getButtonPreferredSize(Button b) {
        return getPreferredSize(b, new Image[]{b.getIcon(), b.getRolloverIcon(), b.getPressedIcon()}, null);
    }

    /**
     * @inheritDoc
     */
    public Dimension getCheckBoxPreferredSize(CheckBox cb) {
        if (chkBoxImages != null) {
            return getPreferredSize(cb, new Image[]{cb.getIcon(), cb.getRolloverIcon(), cb.getPressedIcon()}, chkBoxImages[0]);
        }
        Dimension d = getPreferredSize(cb, new Image[]{cb.getIcon(), cb.getRolloverIcon(), cb.getPressedIcon()}, null);

        // allow for checkboxes without a string within them
        d.setHeight(Math.max(8, d.getHeight()));

        d.setWidth(d.getWidth() + d.getHeight() + cb.getGap());
        return d;
    }

    /**
     * @inheritDoc
     */
    public Dimension getLabelPreferredSize(Label l) {
        return getPreferredSize(l, new Image[]{l.getIcon()}, null);
    }

    /**
     * @inheritDoc
     */
    private Dimension getPreferredSize(Label l, Image[] icons, Image stateImage) {
        int prefW = 0;
        int prefH = 0;

        Style style = l.getStyle();
        int gap = l.getGap();
        for (int i = 0; i < icons.length; i++) {
            Image icon = icons[i];
            if (icon != null) {
                prefW = Math.max(prefW, icon.getWidth());
                prefH = Math.max(prefH, icon.getHeight());
            }
        }
        String text = l.getText();
        Font font = style.getFont();
        if (font == null) {
            System.out.println("Missing font for " + l);
            font = Font.getDefaultFont();
        }
        if (text != null && text.length() > 0) {
            //add the text size
            switch (l.getTextPosition()) {
                case Label.LEFT:
                case Label.RIGHT:
                    prefW += font.stringWidth(text);
                    prefH = Math.max(prefH, font.getHeight());
                    break;
                case Label.BOTTOM:
                case Label.TOP:
                    prefW = Math.max(prefW, font.stringWidth(text));
                    prefH += font.getHeight();
                    break;
            }
        }
        //add the state image(relevant for CheckBox\RadioButton)
        if (stateImage != null) {
            prefW += (stateImage.getWidth() + gap);
            prefH = Math.max(prefH, stateImage.getHeight());
        }


        if (icons[0] != null && text != null && text.length() > 0) {
            switch (l.getTextPosition()) {
                case Label.LEFT:
                case Label.RIGHT:
                    prefW += gap;
                    break;
                case Label.BOTTOM:
                case Label.TOP:
                    prefH += gap;
                    break;
            }
        }

        if (prefH != 0) {
            prefH += (style.getPadding(Component.TOP) + style.getPadding(Component.BOTTOM));
        }
        if (prefW != 0) {
            prefW += (style.getPadding(Component.RIGHT) + style.getPadding(Component.LEFT));
        }
        return new Dimension(prefW, prefH);
    }

    /**
     * @inheritDoc
     */
    public Dimension getListPreferredSize(List l) {
        int width = 0;
        int height = 0;
        int selectedHeight;
        int selectedWidth;
        ListModel model = l.getModel();
        int numOfcomponents = Math.max(model.getSize(), l.getMinElementHeight());
        Object prototype = l.getRenderingPrototype();
        if(prototype != null) {
            ListCellRenderer renderer = l.getRenderer();
            Component cmp = renderer.getListCellRendererComponent(l, prototype, 0, false);
            height = cmp.getPreferredH();
            width = cmp.getPreferredW();
            cmp = renderer.getListCellRendererComponent(l, prototype, 0, true);

            selectedHeight = Math.max(height, cmp.getPreferredH());
            selectedWidth = Math.max(width, cmp.getPreferredW());
        } else {
            int hightCalcComponents = Math.min(5, numOfcomponents);
            Object dummyProto = l.getRenderingPrototype();
            if(model.getSize() > 0 && dummyProto == null) {
                dummyProto = model.getItemAt(0);
            }
            ListCellRenderer renderer = l.getRenderer();
            for (int i = 0; i < hightCalcComponents; i++) {
                Object value;
                if(i < model.getSize()) {
                    value = model.getItemAt(i);
                } else {
                    value = dummyProto;
                }
                Component cmp = renderer.getListCellRendererComponent(l, value, i, false);

                height = Math.max(height, cmp.getPreferredH());
                width = Math.max(width, cmp.getPreferredW());
            }
            selectedHeight = height;
            selectedWidth = width;
            if (model.getSize() > 0) {
                Object value = model.getItemAt(0);
                Component cmp = renderer.getListCellRendererComponent(l, value, 0, true);

                selectedHeight = Math.max(height, cmp.getPreferredH());
                selectedWidth = Math.max(width, cmp.getPreferredW());
            }
        }
        int verticalPadding = l.getStyle().getPadding(Component.TOP) + l.getStyle().getPadding(Component.BOTTOM);
        int horizontalPadding = l.getStyle().getPadding(Component.RIGHT) + l.getStyle().getPadding(Component.LEFT);

        if (numOfcomponents == 0) {
            return new Dimension(horizontalPadding, verticalPadding);
        }

        if(l instanceof ComboBox) {
            int boxWidth = l.getStyle().getFont().getHeight() + 2;
            return new Dimension(boxWidth + selectedWidth + horizontalPadding, selectedHeight + verticalPadding);
        } else {
            if (l.getOrientation() == List.VERTICAL) {
                return new Dimension(selectedWidth + horizontalPadding, selectedHeight + (height + l.getItemGap()) * (numOfcomponents - 1) + verticalPadding);
            } else {
                return new Dimension(selectedWidth + (width + l.getItemGap()) * (numOfcomponents - 1) + horizontalPadding, selectedHeight + verticalPadding);
            }
        }
    }
    
    
    /**
     * @inheritDoc
     */
    public Dimension getMonthViewPreferredSize(Component mv) {
        Font f = mv.getStyle().getFont();
        int fh = f.getHeight();
        int dayWidth = f.stringWidth("22");

        int w = (dayWidth + DAY_SPACE_W) * 7;
        int h = (fh + DAY_SPACE_H) * 5;

        return new Dimension(w + mv.getStyle().getPadding(Component.RIGHT) + mv.getStyle().getPadding(Component.LEFT), h + mv.getStyle().getPadding(Component.TOP) + mv.getStyle().getPadding(Component.BOTTOM));
    }

    /**
     * @inheritDoc
     */
    public Dimension getRadioButtonPreferredSize(RadioButton rb) {
        if (rButtonImages != null) {
            return getPreferredSize(rb, new Image[]{rb.getIcon(), rb.getRolloverIcon(), rb.getPressedIcon()}, rButtonImages[0]);
        }
        Dimension d = getPreferredSize(rb, new Image[]{rb.getIcon(), rb.getRolloverIcon(), rb.getPressedIcon()}, null);

        // allow for radio buttons without a string within them
        d.setHeight(Math.max(8, d.getHeight()));

        d.setWidth(d.getWidth() + rb.getStyle().getFont().getHeight());
        return d;
    }

    /*public Dimension getSpinnerPreferredSize(Spinner sp) {
    return null;
    }*/
    /**
     * @inheritDoc
     */
    public Dimension getTextAreaSize(TextArea ta, boolean pref) {
        int prefW = 0;
        int prefH = 0;
        Font f = ta.getStyle().getFont();

        //if this is a text field the preferred size should be the text width
        if (ta.getRows() == 1) {
            prefW = f.stringWidth(ta.getText());
        } else {
            prefW = f.charWidth(TextArea.getWidestChar()) * ta.getColumns();
        }
        int rows;
        if(pref) {
            rows = ta.getActualRows();
        } else {
            rows = ta.getLines();
        }
        prefH = (f.getHeight() + 2) * rows;
        int columns = ta.getColumns();
        String str = "";
        for (int iter = 0; iter < columns; iter++) {
            str += TextArea.getWidestChar();
        }
        prefW = Math.max(prefW, f.stringWidth(str));
        prefH = Math.max(prefH, rows * f.getHeight());

        return new Dimension(prefW + ta.getStyle().getPadding(Component.RIGHT) + ta.getStyle().getPadding(Component.LEFT), prefH + ta.getStyle().getPadding(Component.TOP) + ta.getStyle().getPadding(Component.BOTTOM));
    }

    private void drawComponent(Graphics g, Label l, Image icon, Image stateIcon, int preserveSpaceForState) {
        setFG(g, l);

        int gap = l.getGap();
        int stateIconSize = 0;
        int stateIconYPosition = 0;
        String text = l.getText();
        Style style = l.getStyle();
        Font font = style.getFont();
        int fontHeight = 0;
        if (text == null) {
            text = "";
        }
        if(text.length() > 0){
            fontHeight = font.getHeight();
        }
        
        if (stateIcon != null) {
            stateIconSize = stateIcon.getWidth(); //square image width == height
            stateIconYPosition = l.getY() + l.getStyle().getPadding(Component.TOP) + (l.getHeight() - l.getStyle().getPadding(Component.TOP) - l.getStyle().getPadding(Component.BOTTOM)) / 2 - stateIconSize / 2;
            preserveSpaceForState = stateIconSize + gap;
            g.drawImage(stateIcon, l.getX() + l.getStyle().getPadding(Component.LEFT), stateIconYPosition);
        }

        //default for bottom left alignment
        int x = l.getX() + l.getStyle().getPadding(Component.LEFT) + preserveSpaceForState;
        int y = l.getY() + l.getStyle().getPadding(Component.TOP);

        //set initial x,y position according to the alignment and textPosition
        if (l.getAlignment() == Component.LEFT) {
            switch (l.getTextPosition()) {
                case Label.LEFT:
                case Label.RIGHT:
                    y = y + (l.getHeight() - (l.getStyle().getPadding(Component.TOP) + l.getStyle().getPadding(Component.BOTTOM) + Math.max(((icon != null) ? icon.getHeight() : 0), l.getStyle().getFont().getHeight()))) / 2;
                    break;
                case Label.BOTTOM:
                case Label.TOP:
                    y = y + (l.getHeight() - (l.getStyle().getPadding(Component.TOP) + l.getStyle().getPadding(Component.BOTTOM) + ((icon != null) ? icon.getHeight() + gap : 0) + l.getStyle().getFont().getHeight())) / 2;
                    break;
            }
        } else if (l.getAlignment() == Component.CENTER) {
            switch (l.getTextPosition()) {
                case Label.LEFT:
                case Label.RIGHT:
                    x = x + (l.getWidth() - (preserveSpaceForState +
                            style.getPadding(Component.LEFT) +
                            style.getPadding(Component.RIGHT) +
                            ((icon != null) ? icon.getWidth() : 0) +
                            font.stringWidth(text))) / 2;
                    x = Math.max(x, l.getX() + style.getPadding(Component.LEFT) + preserveSpaceForState);
                    y = y + (l.getHeight() - (style.getPadding(Component.TOP) +
                            style.getPadding(Component.BOTTOM) +
                            Math.max(((icon != null) ? icon.getHeight() : 0),
                            fontHeight))) / 2;
                    break;
                case Label.BOTTOM:
                case Label.TOP:
                    x = x + (l.getWidth() - (preserveSpaceForState + style.getPadding(Component.LEFT) +
                            style.getPadding(Component.RIGHT) +
                            Math.max(((icon != null) ? icon.getWidth() : 0),
                            font.stringWidth(text)))) / 2;
                    x = Math.max(x, l.getX() + style.getPadding(Component.LEFT) + preserveSpaceForState);
                    y = y + (l.getHeight() - (style.getPadding(Component.TOP) +
                            style.getPadding(Component.BOTTOM) +
                            ((icon != null) ? icon.getHeight() + gap : 0) +
                            fontHeight)) / 2;
                    break;
            }
        } else if (l.getAlignment() == Component.RIGHT) {
            switch (l.getTextPosition()) {
                case Label.LEFT:
                case Label.RIGHT:
                    x = l.getX() + l.getWidth() - style.getPadding(Component.RIGHT) -
                            (preserveSpaceForState + ((icon != null) ? (icon.getWidth() + gap) : 0) +
                            font.stringWidth(text));
                    x = Math.max(x, l.getX() + style.getPadding(Component.LEFT) + preserveSpaceForState);

                    y = y + (l.getHeight() - (style.getPadding(Component.TOP) +
                            style.getPadding(Component.BOTTOM) +
                            Math.max(((icon != null) ? icon.getHeight() : 0),
                            fontHeight))) / 2;
                    break;
                case Label.BOTTOM:
                case Label.TOP:
                    x = l.getX() + l.getWidth() - style.getPadding(Component.RIGHT) -
                            preserveSpaceForState - (Math.max(((icon != null) ? (icon.getWidth()) : 0),
                            font.stringWidth(text)));
                    x = Math.max(x, l.getX() + style.getPadding(Component.LEFT) + preserveSpaceForState);
                    y = y + (l.getHeight() - (style.getPadding(Component.TOP) +
                            style.getPadding(Component.BOTTOM) +
                            ((icon != null) ? icon.getHeight() + gap : 0) + fontHeight)) / 2;
                    break;
            }
        }


        int textSpaceW = l.getWidth() - style.getPadding(Label.RIGHT) - style.getPadding(Label.LEFT);

        if (icon != null && (l.getTextPosition() == Label.RIGHT || l.getTextPosition() == Label.LEFT)) {
            textSpaceW = textSpaceW - icon.getWidth();
        }

        if (stateIcon != null) {
            textSpaceW = textSpaceW - stateIcon.getWidth();
        } else {
            textSpaceW = textSpaceW - preserveSpaceForState;
        }

        if (icon == null) { // no icon only string 
            drawLabelString(g, l, text, x, y, textSpaceW, preserveSpaceForState);
        } else {
            //int strHeight = l.getStyle().getFont().getHeight();
            int strWidth = l.getStyle().getFont().stringWidth(text);
            int iconWidth = icon.getWidth();
            int iconHeight = icon.getHeight();
            int iconStringWGap;
            int iconStringHGap;

            switch (l.getTextPosition()) {
                case Label.LEFT:
                    if (iconHeight > fontHeight) {
                        iconStringHGap = (iconHeight - fontHeight) / 2;
                        //drawLabelStringValign(g, l, text, x, y, iconStringHGap, iconHeight);
                        strWidth = drawLabelStringValign(g, l, text, x, y, iconStringHGap, iconHeight, textSpaceW, fontHeight, preserveSpaceForState);

                        g.drawImage(icon, x + strWidth + gap, y);
                    } else {
                        iconStringHGap = (fontHeight - iconHeight) / 2;
                        //drawLabelString(g, l, text, x, y);
                        strWidth = drawLabelString(g, l, text, x, y, textSpaceW, preserveSpaceForState);

                        g.drawImage(icon, x + strWidth + gap, y + iconStringHGap);
                    }
                    break;
                case Label.RIGHT:
                    if (iconHeight > fontHeight) {
                        iconStringHGap = (iconHeight - fontHeight) / 2;
                        g.drawImage(icon, x, y);
                        //drawLabelStringValign(g, l, text, x+ iconWidth + gap, y, iconStringHGap, iconHeight);
                        drawLabelStringValign(g, l, text, x + iconWidth + gap, y, iconStringHGap, iconHeight, textSpaceW, fontHeight, preserveSpaceForState);
                    } else {
                        iconStringHGap = (fontHeight - iconHeight) / 2;
                        g.drawImage(icon, x, y + iconStringHGap);
                        //drawLabelString(g, l, text, x + iconWidth + gap, y);
                        drawLabelString(g, l, text, x + iconWidth + gap, y, textSpaceW, preserveSpaceForState);
                    }
                    break;
                case Label.BOTTOM:
                    if (iconWidth > strWidth) { //center align the smaller

                        iconStringWGap = (iconWidth - strWidth) / 2;
                        g.drawImage(icon, x, y);
                        //drawLabelString(g, l, text, x + iconStringWGap, y + iconHeight + gap);
                        drawLabelString(g, l, text, x + iconStringWGap, y + iconHeight + gap, textSpaceW, preserveSpaceForState);
                    } else {
                        iconStringWGap = (strWidth - iconWidth) / 2;
                        g.drawImage(icon, x + iconStringWGap, y);
                        //drawLabelString(g, l, text, x, y + iconHeight + gap);
                        drawLabelString(g, l, text, x, y + iconHeight + gap, textSpaceW, preserveSpaceForState);
                    }
                    break;
                case Label.TOP:
                    if (iconWidth > strWidth) { //center align the smaller

                        iconStringWGap = (iconWidth - strWidth) / 2;
//                        drawLabelString(g, l, text, x + iconStringWGap, y);
                        drawLabelString(g, l, text, x + iconStringWGap, y, textSpaceW, preserveSpaceForState);
                        g.drawImage(icon, x, y + fontHeight + gap);
                    } else {
                        iconStringWGap = (strWidth - iconWidth) / 2;
//                        drawLabelString(g, l, text, x, y);
                        drawLabelString(g, l, text, x, y, textSpaceW, preserveSpaceForState);
                        g.drawImage(icon, x + iconStringWGap, y + fontHeight + gap);
                    }
                    break;
            }
        }
    }

    /**
     * Implements the drawString for the text component and adjust the valign
     * assuming the icon is in one of the sides
     */
    private int drawLabelStringValign(Graphics g, Label l, String str, int x, int y,
            int iconStringHGap, int iconHeight, int textSpaceW, int fontHeight, int preserveSpaceForState) {
        switch (l.getVerticalAlignment()) {
            case Component.TOP:
                return drawLabelString(g, l, str, x, y, textSpaceW, preserveSpaceForState);
            case Component.CENTER:
                return drawLabelString(g, l, str, x, y + iconHeight / 2 - fontHeight / 2, textSpaceW, preserveSpaceForState);
            default:
                return drawLabelString(g, l, str, x, y + iconStringHGap, textSpaceW, preserveSpaceForState);
        }
    }

    /**
     * Implements the drawString for the text component and adjust the valign
     * assuming the icon is in one of the sides
     */
    private int drawLabelString(Graphics g, Label l, String text, int x, int y, int textSpaceW, int preserveSpaceForState) {
        Style style = l.getStyle();

        int cx = g.getClipX();
        int cy = g.getClipY();
        int cw = g.getClipWidth();
        int ch = g.getClipHeight();

        int txtX;
        if (l.getTextPosition() == l.RIGHT) {
            txtX = l.getX() + (l.getWidth() - style.getPadding(l.LEFT) - textSpaceW);
            g.clipRect(l.getX() + (l.getWidth() - style.getPadding(l.LEFT) - textSpaceW), cy, textSpaceW, ch);
        } else {
            txtX = x + preserveSpaceForState;
        }

        g.clipRect(txtX, cy, textSpaceW, ch);

        if (l.isTickerRunning()) {
            if (l.getShiftText() > 0) {
                if (l.getShiftText() > textSpaceW) {
                    l.setShiftText(x - l.getX() - style.getFont().stringWidth(text));
                }
            } else if ((x) + l.getShiftText() + style.getFont().stringWidth(text) < txtX) {
                l.setShiftText(textSpaceW - (x - txtX));// + style.getFont().stringWidth(text));

            }
        }
        //g.drawString(text, l.getShiftText() + x, y);
        int drawnW = drawLabelText(g, l, text, x, y, textSpaceW);

        g.setClip(cx, cy, cw, ch);

        return drawnW;
    }

    /**
     * Draws the text of a label
     * 
     * @param g graphics context
     * @param l label component
     * @param text the text for the label
     * @param x position for the label
     * @param y position for the label
     * @param textSpaceW the width available for the component
     * @return the space used by the drawing
     */
    protected int drawLabelText(Graphics g, Label l, String text, int x, int y, int textSpaceW) {
        /** 
         * BlueWhaleSystems fix: Darren Jones
         *
         * - ticket:3538 "LWUIT: Optimise DefaultLookAndFeel.drawLabelText()"
         */
        int finalWidth = textSpaceW;
        if (!l.isTickerRunning()) {
            Font f = l.getStyle().getFont();
            int txtW = f.stringWidth(text);
            
            //if there is no space to draw the text add ... at the end
            if (txtW > textSpaceW && textSpaceW > 0) {
                if (l.isEndsWith3Points()) {
                    String points = "...";
                    
                    float averageCharsPerPixel = ((float)text.length()) / ((float)txtW);
                    int pointsW = f.stringWidth(points);

                    int startIndex = 0;
                    int endIndex = text.length();
                    int newIndex = (int)(averageCharsPerPixel * (textSpaceW - pointsW));
                    while (endIndex - startIndex > 1) {
                        String tmp = text.substring(0, newIndex);
                        
                        int newWidth = f.stringWidth(tmp) + pointsW;
                        if (newWidth < textSpaceW) {
                            startIndex = newIndex;
                            newIndex = startIndex + Math.max(1, (int)(averageCharsPerPixel * (textSpaceW - newWidth)));
                            newIndex = Math.min(newIndex, endIndex - 1);
                            finalWidth = newWidth;
                        }
                        else {
                            endIndex = newIndex;
                            newIndex = endIndex - Math.max(1, (int)(averageCharsPerPixel * (newWidth - textSpaceW)));
                            newIndex = Math.max(newIndex, startIndex + 1);
                        }
                    }
                    text = text.substring(0, startIndex) + points;
                }
                
            }
        }

        g.drawString(text, l.getShiftText() + x, y);
        return Math.min(finalWidth, textSpaceW);
    }


    /**
     * @inheritDoc
     */
    public long findDayAt(int x, int y, com.sun.lwuit.Calendar cal, Component mv) {
        Calendar calendar = Calendar.getInstance();
        long firstOfMonth = getMonthExtent(calendar, cal.getDate(), true);

        int w = mv.getWidth();

        Font f = mv.getStyle().getFont();
        int fh = f.getHeight();

        int labelH = (fh + DAY_SPACE_H);

        int dayWidth = (w / 7);
        long currentDay = firstOfMonth;
        calendar.setTime(new Date(currentDay));
        int dayX = (mv.getAbsoluteX() + (dayWidth * (calendar.get(java.util.Calendar.DAY_OF_WEEK) - 1)));
        int dayY = mv.getAbsoluteY() + labelH;
        
        int h = mv.getHeight();
        int ch = h - labelH;

        int dayHeight = (ch / 6);

        while (!(dayX < x && dayX + dayWidth > x && dayY < y && dayY + dayHeight > y)) {
            currentDay += DAY;
            calendar.setTime(new Date(currentDay));
            dayX = (mv.getAbsoluteX() + (dayWidth * (calendar.get(java.util.Calendar.DAY_OF_WEEK) - 1)));
            if (calendar.get(java.util.Calendar.DAY_OF_WEEK) == Calendar.SUNDAY) {
                dayX = mv.getAbsoluteX();
                dayY += dayHeight;
            }
            if (dayX > mv.getX() + mv.getWidth() && dayY > mv.getY() + mv.getHeight()) {
                return currentDay;
            }
        }
        return currentDay;
    }

    private void paintDates(Graphics g, Date date, Component mv, long sd, Font f,
            int dayWidth, int dayHeight, int labelH,
            int fix, int fh, long selectedDay) {
        int w = f.stringWidth("22");
        Calendar calendar = Calendar.getInstance();
        date.setTime(sd);
        long firstOfMonth = getMonthExtent(calendar, date, true);

        date.setTime(sd);
        long endOfMonth = getMonthExtent(calendar, date, false);

        long day = firstOfMonth;
        int d = 1;
        int sats = 0;
        int dx;
        g.setColor(mv.getStyle().getFgColor());
        do {
            date.setTime(day);
            calendar.setTime(date);
            String dayString = Integer.toString(d);

            int dow = calendar.get(Calendar.DAY_OF_WEEK);

            dx = mv.getX() + (dayWidth * (dow - 1)) + fix;//(d < 10 ? fix : 0);

            int dy = mv.getY() + ((sats) * dayHeight) + labelH+2;

            if (day == selectedDay) {
                g.setColor(mv.getSelectedStyle().getBgColor());
                g.fillRoundRect(dx - 2, dy - 2,
                        w + 5, fh + 4, 4, 4);
                g.setColor(mv.getSelectedStyle().getFgColor());
                g.drawString(dayString, w / 2 - f.stringWidth(dayString) / 2 + dx + 2, dy);
                g.drawRoundRect(dx - 2, dy - 2,
                         w + 5, fh + 4, 4, 4);
            } else {
                g.drawString(dayString, w / 2 - f.stringWidth(dayString) / 2 + dx + 2, dy);
            }

            g.setColor(mv.getStyle().getFgColor());


            day += DAY;
            d++;

            if (dow == Calendar.SATURDAY) {
                sats++;
                g.setColor(mv.getStyle().getBgColor());
                g.fillRect(mv.getX() + 2, dy + fh + 3, mv.getWidth() - 4, 1);
                g.setColor(mv.getStyle().getFgColor());
            }
        } while (day <= endOfMonth);
    }

    /*
     * Finds the first or last day of the month prior or ahead of the given date.
     */
    private long getMonthExtent(Calendar calendar, Date d, boolean first) {
        long day = d.getTime();
        d.setTime(day);
        calendar.setTime(d);
        long origDay = day;

        int currentMonth = calendar.get(Calendar.MONTH);
        int adjacentMonth;
        do {
            long adjacentDay = day + ((first ? -1 : 1) * DAY);
            d.setTime(adjacentDay);
            calendar.setTime(d);
            adjacentMonth = calendar.get(Calendar.MONTH);
            if (currentMonth == adjacentMonth) {
                day = adjacentDay;
            }
        } while (currentMonth == adjacentMonth);

        d.setTime(origDay);
        calendar.setTime(d);
        return day;
    }

    /**
     * @inheritDoc
     */
    public Dimension getComboBoxPreferredSize(ComboBox cb) {
        return getListPreferredSize(cb);
    }

    /**
     * @inheritDoc
     */
    public void drawTabbedPane(Graphics g, TabbedPane tp) {
    }

    /**
     * @inheritDoc
     */
    public Component getTabbedPaneCell(final TabbedPane tp,
            final String text, final Image icon, final boolean isSelected,
            final boolean cellHasFocus, final Style cellStyle, final Style cellSelectedStyle,
            final Style tabbedPaneStyle, final int cellOffsetX,
            final int cellOffsetY, final Dimension cellsPreferredSize,
            final Dimension contentPaneSize) {
        Label cell = new Label(text) {
            public void paint(Graphics g) {
                int tPBorder = tp.getTabbedPaneBorderWidth();
                int focusMarkWidth = tPBorder * 2;
                int tabP = tp.getTabPlacement();
                // Initialize forground colors before calling to super.paint()
                if (isSelected && cellHasFocus) {
                    focusMarkWidth = tPBorder * 3;
                    this.getStyle().setFgColor(cellSelectedStyle.getFgColor());
                } else {
                    this.getStyle().setFgColor(cellStyle.getFgColor());
                }

                super.paint(g);

                if (!isSelected) {
                    g.setColor(0);
                    g.fillRect(getX(), getY(), getWidth(), getHeight(), (byte) 0x2f);
                }
                // coloring back the focus mark line
                g.setColor(getStyle().getFgColor());
                if (tabP == tp.TOP || tabP == tp.BOTTOM) {
                    if (tabP == tp.TOP) {
                        if (isSelected) {
                            g.fillRect(getX(), getY() + tPBorder, getWidth(), focusMarkWidth);// north

                        }
                        g.setColor(tabbedPaneStyle.getFgColor());
                        g.fillRect(getX(), getY(), getWidth(), tPBorder);//north line

                        g.fillRect(getX(), getY(), tPBorder, getHeight());// west line

                    } else {
                        if (isSelected) {
                            g.fillRect(getX(), getY() + getHeight() - focusMarkWidth, getWidth(), focusMarkWidth);// south

                        }
                        g.setColor(tabbedPaneStyle.getFgColor());
                        g.fillRect(getX(), getY() + getHeight() - tPBorder, getWidth(), tPBorder);//south line

                        g.fillRect(getX(), getY(), tPBorder, getHeight());// west line

                    }
                    int x = getX() - cellOffsetX + getWidth();
                    if (x == contentPaneSize.getWidth()) {
                        g.fillRect(x + cellOffsetX - tPBorder, getY(), tPBorder, getHeight());// east line

                    }
                    if (cellsPreferredSize.getWidth() < contentPaneSize.getWidth() && (getX() + getWidth() == cellsPreferredSize.getWidth())) {
                        g.fillRect(getX() + getWidth() - tPBorder, getY(), tPBorder, getHeight());
                    }
                } else { // LEFT or RIGHT

                    if (isSelected) {
                        g.fillRect(getX(), getY() + tPBorder, getWidth(), focusMarkWidth);// north

                    }
                    g.setColor(tabbedPaneStyle.getFgColor());
                    g.fillRect(getX(), getY(), getWidth(), tPBorder);
                    int y = getY() - cellOffsetY + getHeight();
                    if (y == contentPaneSize.getHeight()) {
                        g.fillRect(getX(), y + cellOffsetY - tPBorder, getWidth(), tPBorder);
                    }
                    if (cellsPreferredSize.getHeight() < contentPaneSize.getHeight() && (getY() + getHeight() == cellsPreferredSize.getHeight())) {
                        g.fillRect(getX(), getY() + getHeight() - tPBorder, getWidth(), tPBorder);//south line

                    }
                    if (tabP == tp.LEFT) {
                        g.fillRect(getX(), getY(), tPBorder, getHeight());// west border

                    } else {
                        g.fillRect(getX() + getWidth() - tPBorder, getY(), tPBorder, getHeight());// east border
                    }
                }
            }
        };

        cell.setCellRenderer(true);
        cell.getStyle().setBorder(null);
        cell.getStyle().setMargin(0, 0, 0, 0);
        cell.setIcon(icon);
        updateCellLook(tp, (Component) cell, isSelected);
        if (isSelected) {
            cellStyle.setBgColor(cellStyle.getBgColor());
        }

        cell.setAlignment(Label.CENTER);
        return cell;
    }

    private void updateCellLook(TabbedPane tp, Component c, boolean selected) {
        c.getStyle().setFgColor(tp.getStyle().getFgColor());
        c.getStyle().setBgTransparency(tp.getStyle().getBgTransparency());
        c.getStyle().setFont(tp.getStyle().getFont());
    }

    /**
     * @inheritDoc
     */
    public void drawTabbedPaneContentPane(final TabbedPane tp,
            final Graphics g, final Rectangle rect,
            final Dimension cellsPreferredSize, final int numOfTabs,
            final int selectedTabIndex, final Dimension tabsSize,
            final int cellOffsetX, final int cellOffsetY) {
        int tPBorder = tp.getTabbedPaneBorderWidth();
        if(tPBorder < 1) {
            return;
        }
        int x = rect.getX();
        int y = rect.getY();
        int w = rect.getSize().getWidth();
        int h = rect.getSize().getHeight();
        int listPreferredW = cellsPreferredSize.getWidth();
        int listPreferredH = cellsPreferredSize.getHeight();
        int maxTabWidth = tabsSize.getWidth();
        int maxTabHeight = tabsSize.getHeight();

        g.setColor(tp.getStyle().getBgColor());
        g.fillRect(x, y, w, h, tp.getStyle().getBgTransparency());

        // paint borders for TOP tab placement
        g.setColor(tp.getStyle().getFgColor());
        int tabP = tp.getTabPlacement();

        if (tabP == tp.TOP || tabP == tp.BOTTOM) {
            g.fillRect(x, y, tPBorder, h);// west border

            g.fillRect(x + w - tPBorder, y, tPBorder, h);// east border

            int relativeY = y;
            if (tabP == tp.BOTTOM) {
                relativeY = y + h - tPBorder;
                g.fillRect(x, y, w, tPBorder);// north border

            } else {
                g.fillRect(x, y + h - tPBorder, w, tPBorder);// south border

            }
            if (listPreferredW < w) {
                g.fillRect(listPreferredW - tPBorder, relativeY, w - listPreferredW, tPBorder);
            }
            for (int i = 0; i < numOfTabs; i++) {
                if (i != selectedTabIndex) {
                    g.fillRect((x - cellOffsetX + (maxTabWidth * i)), relativeY, maxTabWidth + tPBorder, tPBorder);
                }
            }
        } else {//if (tabP == LEFT || tabP == RIGHT) {

            g.fillRect(x, y, w, tPBorder);// north border

            g.fillRect(x, y + h - tPBorder, w, tPBorder);// south border

            int relativeX = x;
            if (tabP == tp.RIGHT) {
                g.fillRect(x, y, tPBorder, h);// west border

                relativeX = x + w - tPBorder;
            } else {
                g.fillRect(x + w - tPBorder, y, tPBorder, h);// east border

            }
            if (listPreferredH < h) {
                g.fillRect(relativeX, y + listPreferredH - tPBorder, tPBorder, h - listPreferredH + tPBorder);
            }
            for (int i = 0; i < numOfTabs; i++) {
                if (i != selectedTabIndex) {
                    g.fillRect(relativeX, (y - cellOffsetY + (maxTabHeight * i)), tPBorder, maxTabHeight + tPBorder);
                }
            }
        }
    }

    /**
     * Similar to getText() but works properly with password fields
     */
    protected String getTextFieldString(TextField ta) {
        String text = (String) ta.getText();
        String displayText = "";
        if ((ta.getConstraint() & TextArea.PASSWORD) != 0) {
            // show the last character in a password field
            if (ta.isPendingCommit()) {
                if (text.length() > 0) {
                    for (int j = 0; j < text.length() - 1; j++) {
                        displayText += "*";
                    }
                    displayText += text.charAt(text.length() - 1);
                }
            } else {
                for (int j = 0; j < text.length(); j++) {
                    displayText += "*";
                }
            }
        } else {
            displayText = text;
        }
        return displayText;
    }
    
    /**
     * @inheritDoc
     */
    public void drawTextField(Graphics g, TextField ta) {
        setFG(g, ta);

        // display ******** if it is a password field
        String displayText = getTextFieldString(ta);

        Style style = ta.getStyle();
        int x = 0;
        int cursorCharPosition = ta.getCursorPosition();        
        Font f = ta.getStyle().getFont();
        int cursorX = 0;
        int xPos = 0;
        String inputMode = ta.getInputMode();
        int inputModeWidth = f.stringWidth(inputMode);
        if (cursorCharPosition > 0) {        	
        	if( cursorCharPosition  > ta.getWidth() - inputModeWidth -style.getPadding(Component.LEFT) * 2){
        		ta.setCursorPosition(ta.getWidth() - inputModeWidth -style.getPadding(Component.LEFT) * 2);
        	}
            xPos = f.stringWidth(displayText.substring(0, cursorCharPosition));
            cursorX = ta.getX() + style.getPadding(Component.LEFT) + xPos;
            if (ta.getWidth() > (f.getHeight() * 2) && cursorX >= ta.getWidth()-inputModeWidth - style.getPadding(Component.LEFT)) {
                while (x + xPos >= ta.getWidth() - inputModeWidth -style.getPadding(Component.LEFT) * 2) {
                    x--;
                }
            }
        }

        // BlueWhaleSystems fix - Michael Maguire - 04 Sep 2009
        //
        // Added a hint to the user that a multitap entry is still in progress
        // in a TextField based on 'sophiasmth' suggestion in
        // http://forums.java.net/jive/thread.jspa?threadID=66325&tstart=0
        //
        // Shows a highlight of the character currently being entered.
        // 
        if(ta.isPendingCommit()) {
            String headText = displayText.substring(0, displayText.length()-1);
            g.drawString(headText, ta.getX() + x + style.getPadding(Component.LEFT), 
                    ta.getY() + style.getPadding(Component.TOP));
            g.setColor(style.getFgColor());
            String lastText = displayText.substring(displayText.length()-1);
            g.fillRect(ta.getX() + x + style.getPadding(Component.LEFT) + f.stringWidth(headText), ta.getY() + style.getPadding(Component.TOP), f.stringWidth(lastText), f.getHeight());
            g.setColor(style.getBgColor());
            g.drawString(lastText, ta.getX() + x + style.getPadding(Component.LEFT) + f.stringWidth(headText), ta.getY() + style.getPadding(Component.TOP));
            g.setColor(style.getFgColor());
        }
        else {
            g.drawString(displayText, ta.getX() + x + style.getPadding(Component.LEFT),
                    ta.getY() + style.getPadding(Component.TOP));
        }

        // no point in showing the input mode when there is only one input mode...
        if(ta.getInputModeOrder() != null && ta.getInputModeOrder().length > 1) {
            
            if (ta.handlesInput() && ta.getWidth() / 2 > inputModeWidth) {
            	
                int drawXPos = ta.getX() + style.getPadding(Component.LEFT);
//                if (xPos < ta.getWidth() / 2) {
                    // draw on the right side
                    drawXPos = drawXPos + ta.getWidth() - inputModeWidth - style.getPadding(Component.RIGHT) - style.getPadding(Component.LEFT);
//                }
                g.setColor(style.getFgColor());
                g.fillRect(drawXPos, ta.getY() + style.getPadding(Component.TOP), inputModeWidth,
                        ta.getHeight(), (byte) 140);
                g.setColor(style.getBgColor());
                g.drawString(inputMode, drawXPos, ta.getY() + style.getPadding(Component.TOP));
            }
        }
    }

    /**
     * @inheritDoc
     */
    public Dimension getTextFieldPreferredSize(TextField ta) {
        return getTextAreaSize(ta, true);
    }

    /**
     * @inheritDoc
     */
     public void drawTextFieldCursor(Graphics g, TextField ta) {
        Style style = ta.getStyle();
        Font f = style.getFont();

        // display ******** if it is a password field
        String displayText = getTextFieldString(ta);
        String inputMode = ta.getInputMode();
        int inputModeWidth = f.stringWidth(inputMode);

        int xPos = 0;
        int cursorCharPosition = ta.getCursorPosition();
        if (cursorCharPosition > 0) {
            xPos = f.stringWidth(displayText.substring(0, cursorCharPosition));
        }
        int cursorX = ta.getX() + style.getPadding(Component.LEFT) + xPos;
        int cursorY = ta.getY() + style.getPadding(Component.TOP);
        int x = 0;
        if (ta.getWidth() > (f.getHeight() * 2) && cursorX >= ta.getWidth() - inputModeWidth  -style.getPadding(Component.LEFT)) {
            while (x + xPos >= ta.getWidth() - inputModeWidth - style.getPadding(Component.LEFT) * 2) {
                x--;
            }
        }
        int oldColor = g.getColor();
        g.setColor(style.getFgColor());
        g.drawLine(cursorX + x, cursorY, cursorX + x, cursorY + f.getHeight());
        g.setColor(oldColor);
    }

    /**
     * @inheritDoc
     */
    public void focusGained(Component cmp) {
        if(cmp instanceof Label) {
            Label l = (Label) cmp;
            if (l.shouldTickerStart()) {
                ((Label) cmp).startTicker(getTickerSpeed(), true);
            }
        }
    }

    /**
     * @inheritDoc
     */
    public void focusLost(Component cmp) {
        if(cmp instanceof Label) {
            Label l = (Label) cmp;
            if (l.isTickerRunning()) {
                l.stopTicker();
            }
        }
    }

    /**
     * @inheritDoc
     */
    public void drawRichTextArea( Graphics aGraphics, TextArea aTextArea, char[] aChars, int[] aLineData, int aLineDataCount )
    {
        setFG( aGraphics, aTextArea );

        Style style = aTextArea.getStyle();
        Font font = style.getFont();
        int clipX = aGraphics.getClipX();
        int clipY = aGraphics.getClipY();
        int clipWidth = aGraphics.getClipWidth();
        int clipHeight = aGraphics.getClipHeight();
        int x = aTextArea.getX() + style.getPadding( Component.LEFT );
        int y = aTextArea.getY() + style.getPadding( Component.TOP );
        int width = aTextArea.getWidth();
        int rowHeight = font.getHeight() + aTextArea.getRowsGap();
        int charsOffset = 0;

        for( int index = 0; index < aLineDataCount; index++ )
        {
            if( Rectangle.intersects( x, y, width, rowHeight, clipX, clipY, clipWidth, clipHeight ) )
            {
                aGraphics.drawChars( aChars, charsOffset, aLineData[index], x, y  );
            }

            y += rowHeight;
            charsOffset += aLineData[index];
        }
    }
    
    /**
     * @inheritDoc
     */
    public void drawRichTextArea2( Graphics aGraphics, RichTextArea aRichTextArea )
    {
        setFG( aGraphics, aRichTextArea );

        RichTextAreaModel model = aRichTextArea.getModel();
        Style style = aRichTextArea.getStyle();
        int clipX = aGraphics.getClipX();
        int clipY = aGraphics.getClipY();
        int clipWidth = aGraphics.getClipWidth();
        int clipHeight = aGraphics.getClipHeight();
        int defaultCursorX = aRichTextArea.getX() + style.getPadding( Component.LEFT );
        int cursorX = defaultCursorX;
        int cursorY = aRichTextArea.getY() + style.getPadding( Component.TOP );
        int width = aRichTextArea.getWidth();
        
        Font font = null;
        int[] parameters = new int[3];
        char[] data = null;
        int dataLength, dataIndex, segmentType, segmentLength, rowHeight, rowGap;
        Rectangle rectangle = new Rectangle();
        
        // Ask for data, optionally pre-positioned to a specific scroll position (currently not used).
        data = model.getData( width, 0, parameters );
        dataLength = parameters[RichTextAreaModel.PARAMETER_VALID_LENGTH];
        dataIndex = parameters[RichTextAreaModel.PARAMETER_OFFSET_FOR_POSITION];
        cursorY += parameters[RichTextAreaModel.PARAMETER_HEIGHT_AT_OFFSET_FOR_POSITION];
        
        // Set some defaults. These may be the very first things overridden as the data is read.
        font = aRichTextArea.getDefaultFont();
        rowHeight = font.getHeight();
        rowGap = aRichTextArea.getRowsGap();
        
        aGraphics.setFont( font );
        aGraphics.setColor( style.getFgColor() );
        
        //System.out.println( "drawRichTextArea2 component[" + aRichTextArea + "] clip[" + clipX + "," + clipY + "," + clipWidth + ", " + clipHeight + "]" );
        
        while( dataIndex < dataLength )
        {
            // At the very least, the segment length includes the 'type' character we're about to examine.
            segmentLength = 1;                
            segmentType = data[dataIndex];
            
            switch( segmentType )
            {
                case RichTextArea.INSTRUCTION_DRAW_TEXT:
                case RichTextArea.INSTRUCTION_DRAW_TEXT_LINE:
                    // One char for the length of text, one char for width of text in pixels and the text itself.
                    segmentLength += ( 2 + data[dataIndex + 1] );
                    
                    if( Rectangle.intersects( cursorX, cursorY, width, rowHeight, clipX, clipY, clipWidth, clipHeight ) )
                    {
                        aGraphics.drawChars( data, dataIndex + 3, data[dataIndex + 1], cursorX, cursorY  );
                    }
                    
                    if( RichTextArea.INSTRUCTION_DRAW_TEXT_LINE == segmentType )
                    {
                        // DRAW_TEXT_LINE is equivalent to DRAW_TEXT followed immediately by NEWLINE.
                        cursorY += rowHeight;
                        cursorX = defaultCursorX;
                    }
                    else
                    {
                        cursorX += data[dataIndex + 2];
                    }
                    
                    break;
                    
                case RichTextArea.INSTRUCTION_NEWLINE:
                    // No additional data.
                    cursorY += rowHeight;
                    cursorX = defaultCursorX;
                    break;
                    
                case RichTextArea.INSTRUCTION_NEWLINE_WITH_X:
                    // One char for the value.
                    segmentLength += 1;
                    cursorY += rowHeight;
                    cursorX = defaultCursorX + data[dataIndex + 1];
                    break;
                    
                case RichTextArea.INSTRUCTION_MOVE_XY:
                    // Two chars for the values.
                    segmentLength += 2;
                    cursorX += data[dataIndex + 1];
                    cursorY += data[dataIndex + 2];
                    break;
                    
                case RichTextArea.INSTRUCTION_SET_FONT:
                    // One char for the value.
                    segmentLength++;
                    font = aRichTextArea.getFont( data[dataIndex + 1] );                    
                    rowHeight = font.getHeight() + aRichTextArea.getRowsGap();
                    aGraphics.setFont( font );
                    break;
                    
                case RichTextArea.INSTRUCTION_SET_COLOR:
                    // Two chars for the value.
                    segmentLength += 2;
                    aGraphics.setColor( RichTextData.readInt( data, dataIndex + 1 ) );
                    break;

                case RichTextArea.INSTRUCTION_DRAW_OBJECT:
                    // Three chars for the object width, height and id.
                    segmentLength += 3;
                    
                    if( Rectangle.intersects( cursorX, cursorY, width, data[dataIndex + 2], clipX, clipY, clipWidth, clipHeight ) )
                    {
                        rectangle.setX( cursorX );
                        rectangle.setY( cursorY );
                        rectangle.getSize().setWidth( data[dataIndex + 1] );
                        rectangle.getSize().setHeight( data[dataIndex + 2] );
                        
                        ( model.getObjectPainter( data[dataIndex + 3] ) ).paint( aGraphics, rectangle );
                    }
                    
                    break;

                case RichTextArea.INSTRUCTION_SET_ROW_GAP:
                    // One char for the value.
                    segmentLength++;
                    rowGap = data[dataIndex + 1];
                    break;

                default:
                    throw new RuntimeException( "unexpected instruction at index[" + dataIndex + "] value[" + data[dataIndex] + "]" );
            }

            dataIndex += segmentLength;
        }
    }

    /**
     * @inheritDoc
     */
    public Dimension getRichTextAreaPreferredSize( Component aRichTextArea )
    {
        RichTextArea instance = (RichTextArea) aRichTextArea;
        RichTextAreaModel model = instance.getModel();
        Style style = instance.getStyle();
        int width = com.sun.lwuit.Display.getInstance().getDisplayWidth();
        int height = null != model ? model.getComponentHeight( width ) : instance.getHeightEstimate( width );

        // Already asking for screen width so there's no need to add padding.
        //width += style.getPadding( Component.RIGHT ) + style.getPadding( Component.LEFT );
        height += style.getPadding( Component.TOP ) + style.getPadding( Component.BOTTOM );
        
        //System.out.println( "getRichTextAreaPreferredSize component[" + aRichTextArea + "] dimension[" + width + "," + height + "]" );

        return new Dimension( width, height );
    }
}
